#include "JWorkingCopies.h"
#include "JVerifyValidate.h"
#include "JSerialize.h"
#include "JException.h"
#include <boost/filesystem.hpp>
#include <algorithm>
#include "JDir.h"
#include "JFileException.h"
#include "JException.h"
#include "JInOutPutFstream.h"
#include "JDefine.h"
#include "JLog.h"
#include "JOperateFileResult.h"

using namespace boost::filesystem;

JWorkingCopies::JWorkingCopies()
{
	this->IsChanged = false;
	this->needInitOperation = true;
}


std::string JWorkingCopies::CreateWorkingCopies(std::string _rootUrl,std::string _author,std::string _password,std::string _fullPath)
{
    this->IsChanged = true;
	std::string errorMsg;
	try
	{
		this->rootUrl = _rootUrl;
		this->author = _author;
		this->password = _password;
		this->rootDir = JDir::Absolute(_fullPath);
		this->jsvnPath = rootDir + PATH_SPLIT +"jsvn";
		this->jsvnFileName = rootDir + PATH_SPLIT + "jsvn"+ PATH_SPLIT +"WorkingCopies";
		CreateWorkingCopiesDir();
		CreateWorkingCopiesFile();
	}
	catch(JException& e)
	{
		errorMsg.append(e.GetMessage());
	}
	return errorMsg;
}

void JWorkingCopies::BasicOperateInit(std::string currentWorkingPath) 
{
    
    this->rootDir = JDir::RecurseUpFindFolder(currentWorkingPath, "jsvn");
    //JLog::Log(JLogMessage,"rootDir:%s\n",this->rootDir.c_str());
    if (!this->rootDir.empty()) 
    {
        this->jsvnPath = rootDir + PATH_SPLIT + "jsvn";
        this->jsvnFileName = rootDir + PATH_SPLIT + "jsvn" + PATH_SPLIT + "WorkingCopies";
        JInOutPutFstream inOutPutFstream(this->jsvnFileName.c_str());
        inOutPutFstream.OpenStream(std::ios::in | std::ios::binary);
        this->ReSerialize(inOutPutFstream);
        inOutPutFstream.CloseStream();
    }
    else 
    {
        //throw JFileReadException() << JFileReadErrorInfo("It's not a working path.");
        throw JException("It's not a working path.");
    }
    needInitOperation = false;
}


JWorkingCopies::~JWorkingCopies(void)
{
	if(IsChanged == true)
	{
		SaveWorkingCopiesToFile();
	}
}

int JWorkingCopies::Serialize( JInOutPut& _strea,int& allSize )
{
	int classSize = 0;
	int selfSize = 0;
	JSerialize::SerializeHead(_strea,selfSize,classSize);
	JSerialize::Serialize(_strea,this->rootUrl,classSize);
	JSerialize::Serialize(_strea,this->author,classSize);
	JSerialize::Serialize(_strea,this->password,classSize);
	JSerialize::Serialize(_strea,this->rootDir,classSize);
	JSerialize::Serialize<JWorkingCopiesBaseFile>(_strea,this->copiesFile,classSize);
        return JSerialize::SerializeTail(_strea,allSize,selfSize,classSize);
}

int JWorkingCopies::ReSerialize( JInOutPut& strea) 
{
    int classSize = JSerialize::ReSerializeHead(strea);
    this->rootUrl = JSerialize::ReSerializeString(strea);
    this->author = JSerialize::ReSerializeString(strea);
    this->password = JSerialize::ReSerializeString(strea);
    this->rootDir = JSerialize::ReSerializeString(strea);
    //this->copiesFileName = JSerialize::ReSerializeStringList(strea);
    this->copiesFile = JSerialize::ReSerializeList<JWorkingCopiesBaseFile > (strea);
    return classSize;
}


int JWorkingCopies::PackStubMessage(JInOutPut& _strea,int& _allSize) 
{
    //和Serialize的不同在于不用序列化copiesFile
    int classSize = 0, selfSize = 0;
    JSerialize::SerializeHead(_strea, selfSize, classSize);
    JSerialize::Serialize(_strea, this->author, classSize);
    JSerialize::Serialize(_strea, this->password, classSize);
    JSerialize::Serialize(_strea,this->rootDir,classSize);
    return JSerialize::SerializeTail(_strea, _allSize, selfSize, classSize);
}


/*服务器解包*/
int JWorkingCopies::UnPackStubMessage(JInOutPut& _strea)
{
    int classSize = JSerialize::ReSerializeHead(_strea);
    this->author = JSerialize::ReSerializeString(_strea);
    this->password = JSerialize::ReSerializeString(_strea);
    this->rootDir = JSerialize::ReSerializeString(_strea);
    return classSize;
}


/*
 * 把JWorkingCopiesBaseFile加入到copiesFile中，并且序列化，
 * 然后把file的指针放入result.workingCopies中返回，这是需要显示的结果
 * 如果isRecurse是true，那么迭代读取
 * 
 */
JOperateFileResult JWorkingCopies::AddFile(std::string fileName, bool isRecurse) 
{
    this->IsChanged = true;
    JOperateFileResult result;
    try 
    {
        fileName = ConvertFullFileName(fileName);
        if (needInitOperation == true) 
        {
            this->BasicOperateInit(JDir::CurrentWorkingPath());
        }
        //It's in working path, so only add "fileName" to to WorkingCopies file.
        if (!IsInSubWorkingPath(fileName)) 
        {
            throw JException(fileName + " is not in working copies."); // << JErrorInfo();
        }
        if (isRecurse == true) 
        {
            std::list<std::string> listFileName = JBaseFile::RecurseDownBaseFile(fileName);
            std::list<std::string>::iterator ite = listFileName.begin();
            for (; ite != listFileName.end(); ++ite) 
            {
                JWorkingCopiesBaseFile file;
                file.operate = JWorkingFileAdd;
                file.lastChangeVersion = -1;
                file.version = -1;
                file.author = this->author;
                file.fullFileName = *ite;
                file.dirOrFile = JBaseFile::FileOrDir(file.fullFileName);
                if (this->copiesFile.end() == std::find(this->copiesFile.begin(), this->copiesFile.end(), file)) 
                {
                    this->copiesFile.push_back(file);
                    std::list<JWorkingCopiesBaseFile>::iterator ite2 = this->copiesFile.end();
                    --ite2;
                    result.workingCopies.push_back(&*ite2);
                }
            }
        }
        else 
        {
            JWorkingCopiesBaseFile file;
            file.operate = JWorkingFileAdd;
            file.lastChangeVersion = -1;
            file.version = -1;
            file.author = this->author;
            file.fullFileName = fileName;
            ///////////////////////////////////////////////////////////////////////////开始没有下面这句，可能是忘了
            this->copiesFile.push_back(file);
            std::list<JWorkingCopiesBaseFile>::iterator ite2 = this->copiesFile.end();
            --ite2;
            result.workingCopies.push_back(&*ite2);
        }
    }
    catch (JException& e) 
    {
        result.errorMsg = e.GetMessage();
    }
    return result;
}

JOperateFileResult JWorkingCopies::AddFile( std::list<std::string> _fullFileNames )
{
	JOperateFileResult result;
	std::list<std::string>::iterator ite = _fullFileNames.begin();
	for(;ite != _fullFileNames.end(); ++ite)
	{
		JWorkingCopiesBaseFile file;
		file.operate = JWorkingFileAdd;
		file.lastChangeVersion = -1;
		file.version = -1;
		file.author = this->author;
		file.fullFileName = *ite;
		if(this->copiesFile.end() == std::find(this->copiesFile.begin(),this->copiesFile.end(),file))
		{
			this->copiesFile.push_back(file);
			std::list<JWorkingCopiesBaseFile>::iterator ite2 = this->copiesFile.end();
			--ite2;
			result.workingCopies.push_back(&*ite2);
		}
	}
	return result;
}

void JWorkingCopies::CreateWorkingCopiesDir()
{
	JDir::CreateDir(this->jsvnPath);
}

void JWorkingCopies::CreateWorkingCopiesFile()
{
	JFile::CreateFile(this->jsvnFileName);
}

JOperateFileResult JWorkingCopies::DeleteFile(std::string fileName) 
{
    this->IsChanged = true;
    JOperateFileResult result;
    try
    {
        if (needInitOperation == true) 
        {
            this->BasicOperateInit(JDir::CurrentWorkingPath());
        }
        fileName = ConvertFullFileName(fileName);
        //printf("delete file %s\n",fileName.c_str());
        if (!IsInSubWorkingPath(fileName)) 
        {
            throw JException(fileName + " is not in working copies.");
        }
        std::list<JWorkingCopiesBaseFile*> listPWorkingCopiesBaseFile = GetChildCopiesFile(fileName);
        std::list<JWorkingCopiesBaseFile*>::iterator ite = listPWorkingCopiesBaseFile.begin();
        for (; ite != listPWorkingCopiesBaseFile.end(); ++ite) 
        {
            if ((*ite)->version != -1) 
            {
                (*ite)->operate = JWorkingFileDelete;
                result.workingCopies.push_back(&**ite);
            } 
            else 
            {
                (**ite).operate = JWorkingFileDelete;
                JWorkingCopiesBaseFile file(**ite);
                this->copiesFile.remove(**ite);
                result.originalNotInWorkingCopies.push_back(file);
            }
        }
    } 
    catch (JException& e) 
    {
        result.errorMsg = e.GetMessage();
    }
    return result;
}

JOperateFileResult JWorkingCopies::ModifyFile( std::string oldFileName,std::string newFileName )
{
    this->IsChanged = true;
	oldFileName = JDir::Absolute(oldFileName);
	newFileName = JDir::Absolute(newFileName);
	JOperateFileResult result = DeleteFile(oldFileName);
	JBaseFile::ReName(oldFileName,newFileName);
	needInitOperation = false;
	
	std::list<std::string> fullFileName;
	std::list<JWorkingCopiesBaseFile*>::iterator ite = result.workingCopies.begin();
	for(;ite != result.workingCopies.end(); ++ite)
	{
		fullFileName.push_back(newFileName + PATH_SPLIT + (*ite)->fullFileName.substr(oldFileName.length()));
	}
	std::list<JWorkingCopiesBaseFile>::iterator ite2 = result.originalNotInWorkingCopies.begin();
	for(;ite2 != result.originalNotInWorkingCopies.end(); ++ite2)
	{
		fullFileName.push_back(newFileName + ite2->fullFileName.substr(oldFileName.length()));
	}
	JOperateFileResult result2 = AddFile(fullFileName);
	//result += result2;
        result.Append(result2);
	return result;
}

//bool JWorkingCopies::IsInWorkingCopies( std::string fileName ) 
//{
//	if(this->copiesFileName.end() != find(this->copiesFileName.begin(),this->copiesFileName.end(),fileName))
//	{
//		return true;
//	}
//	return false;
//}

//std::list<std::string> JWorkingCopies::GetAllFileNames()
//{
//	return this->copiesFileName;
//}

std::string JWorkingCopies::FindRootDir(std::string currentWorkingPath)
{
	std::string rootPath = JDir::RecurseUpFindFolder(currentWorkingPath,"jsvn");
	return rootPath;
}

std::string JWorkingCopies::ConvertFullFileName(std::string _fileName) {
    if (JBaseFile::IsExist(_fileName)) 
    {
        return JBaseFile::Absolute(_fileName);
    }
    else 
    {
        return _fileName;
        //throw JFileNotExistException() << JFileNotExistErrorInfo("Function:ConvertFullFileName error.File not exist.");
        //throw JException() << JErrorInfo("Function:ConvertFullFileName error.File not exist.");
    }
}

bool JWorkingCopies::IsInWorkingPath( std::string _fileName )
{
	if(_fileName.find(this->rootDir) == std::string::npos)
	{
		return false;
	}
	return true;
}

std::string JWorkingCopies::ConvertToFullWorkingFileName( std::string _fullFileName )
{
	path p(_fullFileName);
        std::string s = p.filename().generic_string();
	std::string ss = boost::filesystem::current_path().generic_string();
	return ss + PATH_SPLIT + s;
}

bool JWorkingCopies::IsInSubWorkingPath( std::string fullFileName )
{
	return JBaseFile::HasGenerateFile(this->rootDir,fullFileName);
}

void JWorkingCopies::SaveWorkingCopiesToFile()
{
    //对copiesFile进行排序
    SortCopiesFile();
	int tmp;
        JInOutPutFstream inOutPutFstream(this->jsvnFileName);
        inOutPutFstream.OpenStream(std::fstream::out | std::fstream::binary | std::fstream::trunc);
	Serialize(inOutPutFstream,tmp);
	inOutPutFstream.CloseStream();
}

JOperateFileResult JWorkingCopies::FileStatus(std::string fileName) {
    JOperateFileResult reslut;
    try 
    {
        this->IsChanged = false;
        fileName = ConvertFullFileName(fileName);
        this->BasicOperateInit(fileName);
        std::list<JWorkingCopiesBaseFile>::iterator ite = this->copiesFile.begin();
        for (; ite != this->copiesFile.end(); ++ite) 
        {
            if (IsBaseFileValidate(ite->fullFileName)) 
            {
                reslut.workingCopies.push_back(&(*ite));
            }
            else 
            {
                ite->operate = JWorkingFileNotExist;
                reslut.workingCopies.push_back(&(*ite));
            }
        }
    } 
    catch (JException& e) 
    {
        reslut.errorMsg.append(e.GetMessage());
    }
    return reslut;
}

//std::list<JWorkingCopiesBaseFile*> JWorkingCopies::GetChildCopiesFile(std::string _fileName) 
//{
//    _fileName = ConvertFullFileName(_fileName);
//    //////////////////////////////////////////////////////////////////////////
//}

std::list<JWorkingCopiesBaseFile*> JWorkingCopies::GetChildCopiesFile( std::string _filePath) 
{
    _filePath = ConvertFullFileName(_filePath);
    //std::list<std::list<JWorkingCopiesBaseFile>::iterator > listIterator;
    std::list<JWorkingCopiesBaseFile*> listPWorkingCopiesBaseFile;
    std::list<JWorkingCopiesBaseFile>::iterator ite = this->copiesFile.begin();
    for (; ite != this->copiesFile.end(); ++ite) 
    {
        if (JDir::HasGenerateFile(_filePath, ite->fullFileName)) 
        {
            listPWorkingCopiesBaseFile.push_back(&(*ite));
            //listIterator.push_back(ite);
        }
    }
    return listPWorkingCopiesBaseFile;
}

bool JWorkingCopies::IsBaseFileValidate( std::string _fullFileName )
{
	return JBaseFile::IsExist(_fullFileName);
}

int JWorkingCopies::SerializeWithFile( JInOutPut& _strea,int& allSize )
{
	//std::string rootUrl;
	//std::string author;
	//std::string password;
	//std::string rootDir;
	//std::list<JWorkingCopiesBaseFile> copiesFile;
	//if JWorkingCopiesBaseFile is a dir, serialize
	int classSize = 0;
	int selfSize = 0;
	JSerialize::PutInt(_strea,classSize,selfSize);
	JSerialize::Serialize(_strea,this->rootUrl,classSize);
	JSerialize::Serialize(_strea,this->author,classSize);
	JSerialize::Serialize(_strea,this->password,classSize);
	JSerialize::Serialize(_strea,this->rootDir,classSize);
	JSerialize::Serialize<JWorkingCopiesBaseFile>(_strea,this->copiesFile,classSize);
	selfSize += classSize;
	JSerialize::Verify(_strea,classSize,-1*selfSize);
	allSize += selfSize;
	return selfSize;
}

void JWorkingCopies::SortCopiesFile()
{
    copiesFile.sort();
    //sort(copiesFile.begin(),copiesFile.end());
}

std::string JWorkingCopies::GetPassword()
{
    return this->password;
}

std::string JWorkingCopies::GetUserName()
{
    return this->author;
}

std::string JWorkingCopies::GetRootDir()
{
    return this->rootDir;
}